package com.dynamic.mybatis.core.session;

import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import com.dynamic.mybatis.core.builder.DynamicMapperAdapterBuilder;
import com.dynamic.mybatis.core.datasource.DynamicDataSourceHolder;
import com.dynamic.mybatis.core.enums.ReturnType;
import com.dynamic.mybatis.core.exceptions.MyBatisExecutorException;
import com.dynamic.mybatis.core.exceptions.MybatisBuilderException;
import com.dynamic.mybatis.core.metadata.DynamicConfiguration;
import com.dynamic.mybatis.core.metadata.DynamicMappedStatement;
import com.dynamic.mybatis.core.metadata.DynamicPage;
import com.dynamic.mybatis.core.toolkit.BeanUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @Description
 * @Author xs
 * @Date 2021/3/24 18:02
 */
public class DynamicSqlSession{
    private SqlSession sqlSession;

    public DynamicSqlSession(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    public SqlSession getSqlSession() {
        return sqlSession;
    }

    public <T> T  executeTest(DynamicMappedStatement dms, Object parameter){
        //生成新的临时mybatis上下文
        DynamicConfiguration dcNew = ((DynamicConfiguration)sqlSession.getConfiguration()).clone();
        DynamicMappedStatement dmsNew = BeanUtil.copy(dms);
        //解析并生成mybatis动态sql
        new DynamicMapperAdapterBuilder(dcNew).parser(dmsNew);
        //执行mybatis动态sql
        SqlSession sqlSessionNew;
        DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(dcNew);
        try {
            sqlSessionNew = sqlSessionFactory.openSession(dcNew.getDefaultExecutorType(),true);
        } catch (Exception e) {
            throw new MybatisBuilderException("sqlSessionFactory create error.",e);
        }
        DynamicSqlSession dynamicMybatisSqlSession = new DynamicSqlSession(sqlSessionNew);
        try {
            return dynamicMybatisSqlSession.execute(dms.getId(),null,parameter);
        }finally {
            SqlCommandType sqlCommandType = dcNew.getDynamicMappedStatement(dms.getId()).getSqlCommandType();
            if( sqlCommandType != SqlCommandType.SELECT){
                sqlSessionNew.rollback();
            }
        }
    }

    /**
     * @Description: 执行SQl后不会删除mappedStatement,下次直接调用execute执行
     * @param dms
     * @param parameter 参数(一般使用Map)
     * @param parameter 分页参数说明
     * pageNumber 当前页码从1开始 pageSize 每页数量
     * offset 起始行下标从0开始  limit 筛选数量
     * 示例 {"pageNumber":1,"pageSize":10}或{offset:0,limit:10}
    　*/
    public  <T> T  executeGenerator(DynamicMappedStatement dms,Object parameter){
        //测试语句是否正确
        executeTest(dms,parameter);
        //解析并生成mybatis动态sql
        new DynamicMapperAdapterBuilder(sqlSession.getConfiguration()).parser(dms);
        //执行mybatis动态sql
        return execute(dms.getId(),null,parameter);
    }

    public  <T> T execute(String mappedStatementId){
        return  execute(mappedStatementId,null);
    }
    public  <T> T execute(String mappedStatementId,Object parameter){
        return  execute(mappedStatementId,null,parameter);
    }

    public  <T> T execute(String mappedStatementId,Object dataSourceKey,Object parameter){
        //可能返回该mappedStatementId的父节点DynamicMappedStatement
        DynamicMappedStatement dms = ((DynamicConfiguration)sqlSession.getConfiguration()).getDynamicMappedStatement(mappedStatementId);
        Object targetDataSourceKey = null;
        if(!Objects.isNull(dataSourceKey)){

            targetDataSourceKey = dataSourceKey;

        }else if(!Objects.isNull(dms.getTargetDataSourceKey())){

            targetDataSourceKey = dms.getTargetDataSourceKey();

        }else if(ArrayUtils.isNotEmpty(dms.getDynamicDataSourceKeys())){

            targetDataSourceKey = dms.getDynamicDataSourceKeys()[0];
        }
        if(!Objects.isNull(targetDataSourceKey)){
            DynamicDataSourceHolder.set(targetDataSourceKey);
        }
        try {
           return  doExecute(mappedStatementId,dms.getSqlCommandType(),dms.getReturnType(),parameter);
        }catch (Exception e){
            String message = "execute dynamicSQL error. dynamicSQL code ["+mappedStatementId+"]"
                    +System.lineSeparator()+System.lineSeparator()
                    +"primordial SQL "+System.lineSeparator()+dms.getMappedStatement()
                    +System.lineSeparator()+System.lineSeparator()
                    +"analysis SQL "+System.lineSeparator()+sqlSession.getConfiguration().getMappedStatement(dms.getId()).getBoundSql(parameter).getSql()
                    +System.lineSeparator()+System.lineSeparator();
            throw new MyBatisExecutorException(message,e);
        }finally {
            if(!Objects.isNull(targetDataSourceKey)) {
                //解除数据源和线程绑定
                DynamicDataSourceHolder.clear();
            }
        }
    }

    public   <T> T doExecute(String mappedStatementId, SqlCommandType commandType, ReturnType returnType, Object parameter){
        Object result;
        if(commandType == commandType.SELECT){
            //分页
            Map parame = parameter instanceof  Map ? (Map) parameter : BeanUtils.beanToMap(parameter);
            if((parame != null && (ReturnType.PAGE.name().equals(Objects.toString(parame.get("returnType"))))) || returnType == ReturnType.PAGE ){
                DynamicPage page;
                if(parameter instanceof Map ){
                    page =  new DynamicPage((Map)parameter);
                }else {
                    parameter = BeanUtils.beanToMap(parameter);
                    page =  new DynamicPage((Map)parameter);
                }
                if(page == null){
                    page = new DynamicPage();
                    PageHelper.startPage(page.getPageNumber(), page.getPageSize());
                }else if(page.getOffset() != null){
                    PageHelper.offsetPage(page.getOffset(),page.getLimit());
                }else {
                    PageHelper.startPage(page.getPageNumber(), page.getPageSize());
                }
                List list = sqlSession.selectList(mappedStatementId,parameter);
                PageInfo pageList = new PageInfo(list);
                result =  page.result(pageList.getList(),pageList.getTotal());
                //列表
            }else if((parame != null && (ReturnType.COLLECT.name().equals(Objects.toString(parame.get("returnType"))))) || returnType == ReturnType.COLLECT){
                result = sqlSession.selectList(mappedStatementId,parameter);
                //单条查询
            }else {
                result =  sqlSession.selectOne(mappedStatementId,parameter);
            }
        }else if(commandType == SqlCommandType.INSERT){
            result = sqlSession.insert(mappedStatementId,parameter);
        }else if(commandType == SqlCommandType.UPDATE){
            result = sqlSession.update(mappedStatementId,parameter);
        }else if(commandType == SqlCommandType.DELETE){
            result = sqlSession.delete(mappedStatementId,parameter);
        }else {
            throw new MyBatisExecutorException("sqlCommandType or returnType is unkonw.");
        }
        return (T) result;
    }
}